git 入门
想要完整的掌握git, 请看<
一点提示:Git是一本书, 我们要随时使用 git status 来处理git add/commit/push/merge中遇到的各种问题。
git , 虽然名字上叫 饭桶, 但是实际在功能上却可以秒杀传统的各种SCM, 比如 VSS, CVS, SVN.
git跟上面三种SCM最大的区别,在于: git 可以完全脱离远程服务器。 在本地使用。
工作过程
git 的典型工作过程是;
- 程序员A修改了代码。(通过git status/diff查看修改的情况)
- 程序员A把修改后的文件,(通过git add/rm)放到了git的"待提交代码列表"中。
- 程序员A把“待提交的代码”提交(通过git commit)
- 程序员A把“本地的commit“ 同步到远程的git服务器上(通过git push)
- 程序员B 把程序员A的代码(通过git pull/merge)同步到本地。
几个概念
working directory: 当前的代码目录
branch: 代码的分支。 在本地可以有,在远程也可以有。
commit: 每次的代码的提交。每个commit 都对应一个40位的字符串,例如
e4b924e3e0c03a6d71effc3bc3075c2596c14cf3
staging area: 每次代码在被 git add/rm 之后,都会被放到一个“待提交的区域”, 这里的代码只是一个“待提交状态”。只有在 git commit 之后,本地的git系统才会 认为有一个专门的commit 号 对应刚才提交的代码。
cherrypick:
rebase
merge
push
pull
合并冲突 TODO
Git 极速入门
基本步骤是:
获取最新代码 git pull
提交代码 git add/commit/push
查看历史 git log/show
做代码比较 git diff
查看所有的GIT变量:
$ git var -l
可以载入变量:
git config -f = ~/.gitconfig
提交时需要:(代码review的小要求)
- 有正确的 用户名和电子邮件.
- comments 中就不要有"由XX操作"了。重复。
$ git -config user.name "Jike Song"
$ git -config user.email [email][email protected][/email]
注意,这样会在当前repository目录下的. git /config中写入配置信息。 如果 git -config加了--global 选项,配置信息就会写入到~/. git config文件中。 因为你可能用不同的身份参与不同的项目,而多个 项目都用 git 管理,所以建议不用global配置。
生成本地修改的所有patch(多少次提交就多少个.path文件):
$ git format-patch origin
生成单个patch文件(例子中是将最近5次提交的内容合并到一个文件中):
$ git format-patch -5 --stdout > patch_by_siwei.txt
往远程服务器上提交分支:
$ git push origin [本地分支名]:[远程分知名(push之后就存在了)]
例如:(理论上)
git push origin my_local_branch:lily
提交之后远程就会出现了一个"lily"分支。
apply patch
最好在linux环境下。如果出现的诡异的 /dev/null问题, 十有八九是因为dos文件格式造成的。试试 dos2unix 。 如果还不行的话。。。哎,手工吧! 血的教训: 每天时不时的 update 一下, 绝对不要等最后push的时候再合并。。。痛苦啊。
每天最好更新一下远程服务器中的代码:
$ git pull . master (把远程的master更新到当前的本地分支)
git patch/apply 某一个commit
如何在另外一个机器上 apply某个 path?
我们遇到一个情况, 在某个机器上,需要一个紧急部署,但是又不希望手工去修改(因为已经有了一个commit 了)。这样的情况下,可以使用git path 专门为某个commit 生成patch, 然后在这个远程机器上apply it. ( apply a patch generated from a specific commit ?)
$ git format-patch -1 <sha> # => 0001__.patch
$ git apply <path_file >
# git cherry-pick. 如何把已经提交的commit, 从一个分支放到另一个分支
cherry pick
实际问题: 在本地 master 分支上做了一个commit ( 38361a68138140827b31b72f8bbfd88b3705d77a ) , 如何把它放到 本地 old_cc 分支上?
办法之一: 使用 cherry-pick. 根据git 文档:
就是对已经存在于别的分支的commit 放到当前的分支上。
简单用法:
git cherry-pick <commit id>
例如:
$ git checkout old_cc
$ git cherry-pick 38361a68 # 这个 38361a68 号码,位于:
$ git log
commit 38361a68138140827b31b72f8bbfd88b3705d77a
Author: Siwei Shen <[email protected]>
Date: Sat Dec 10 00:09:44 2011 +0800
如果顺利,就会正常提交。结果:
Finished one cherry-pick.
# On branch old_cc
# Your branch is ahead of 'origin/old_cc' by 3 commits.
如果在cherry-pick 的过程中出现了冲突
Automatic cherry-pick failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>' or 'git rm <paths>'
and commit the result with:
git commit -c 15a2b6c61927e5aed6718de89ad9dafba939a90b
那么我们就当它跟普通的冲突一样,手工解决:
2.1看哪些文件出现冲突
$ git status
both modified: app/models/user.rb
2.2手动解决它。
$ vim app/models/user.rb
2.3
$ git add app/models/user.rb
2.4
git commit -c <新的commit号码>
恢复做过的改动
取消上一次提交?
$ git reset --soft HEAD~1
--soft
表示把当前git的状态(假设是干净的),还原成“产生了改动,保留在待提交区“中。
HEAD~1
表示最新的一个提交。
Changes to be committed:
modified: .gitignore
继续,我们把修改的文件,从“待提交列表”放回到普通状态:
$ git reset HEAD .gitignore
Unstaged changes after reset:
M .gitignore
Changes not staged for commit:
modified: .gitignore
我们可以继续把它的改动恢复:
$ git checkout -- .gitignore
分支的知识( git branch)
查看所有的分支
所有以 "remotes/"开头的分支,都是远程的分支。
* master
表示 当前的分支是 master.
$ git branch -a
sg552@youku:/sg552/workspace/m-cms$ git branch -a
clean_cache
* master
siwei_branch
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/siwei_branch
上面的 origin/HEAD -> origin/master
表示 默认的分支就是origin/master
.
显示本地和远程的origin 上的分支情况
新建分支
$ git branch new_branch
$ git branch -a
# 结果是:
* master
new_branch
切换分支
$ git checkout new_branch
在新的分支上操作代码
跟普通的操作一模一样。
可以认为是两套不同的代码。 他们有相同的基础。
merge : 在两个不同的分支间操作commit
比如,我们把 branch1 中的 commit1.1 放到branch2 上:
$ git checkout branch2
$ git merge branch1
Updating e4b924e..df5529b
Fast-forward
file1 | 3 +++
1 file changed, 3 insertions(+)
然后,我们再
$ git log
就能看到, 原来位于 branch1 中的 commit 1.1 来到了branch2 上。
删掉分支
下面的操作,删掉了 "new_branch" 这个分支。
$ git branch -d new_branch
Deleted branch new_branch (was e4b924e)
显示git push/fetch/pull等的细节。
sg552@youku:/sg552/workspace/m-cms$ git remote show origin
* remote origin
Fetch URL: ssh://shensiwei@gforge.1verge.net:22022/gitroot/m-cms
Push URL: ssh://shensiwei@gforge.1verge.net:22022/gitroot/m-cms
HEAD branch: master
Remote branches:
master tracked
siwei_branch tracked
Local branch configured for 'git pull':
master merges with remote master
Local refs configured for 'git push':
master pushes to master (up to date)
siwei_branch pushes to siwei_branch (fast-forwardable)
git tag 基本操作
查看本地所有的tag
$ git tag -l
列出远程的所有的tag:
$ git ls-remote --tags
From ssh://....
a70cffc64e73ae4b5ab7329480999305f11d9c76 refs/tags/1.0.0
6d1ad6c601652442d6d32fce5233ba50a1dd9f56 refs/tags/1.0.0^{}
1acf5e9aabbd353ff45b7aa3fe319d3822acad19 refs/tags/1.0.1
5bba7a637e64267ffad38dd48c30df00da6cc91e refs/tags/1.0.1^{}
新增tag
名字叫 1.1.0, 对应的commit 是 "a50fabb":
$ git tag -a 1.1.0 a50fabb -m '版本1.1.0'
删掉tag:
例如,删掉名为 '1.0'的tag:
$ git tag -d 1.0
显示tag的详情:
$ git tag -n2 # will show the tags with their messages.
1.0.0 CMS 3.0 的抢先版,....
1.0.1 3月14日下午.修改了若干BUG(大部分都是表单验证)...
把本地的tag推送到远程git服务器上
$ git push --tags
git stash
当我们做一个功能,做到一半时, 来了新需求,该怎么办?
我们可以使用 git stash .把当前的改动保存起来,以备日后使用。
新建 stash
$ git stash save "登陆功能做到一半,完成了视图,控制器未完成"
stash 列表
然后我们使用 git stash list
命令,就可以看到:
$ git stash list
stash@{0}: On master: "登陆功能做到一半,完成了视图,控制器未完成"
恢复 代码
当我们做完另外一件事后,要恢复代码,可以:
$ git stash apply stash@{0}
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: file1
modified: file2
可以看到 工作区 又回到了被改动的状态。改了一半的文件又出来了。
删除 stash
某个 stash 用不到后,可以把它删掉。
$ git stash drop stash@{0}
git log
查看日志列表 例如:
$ git log
commit e4b924e3e0c03a6d71effc3bc3075c2596c14cf3
Author: Shen Siwei <[email protected]>
Date: Tue Oct 13 11:30:31 2015 +0800
增加了登陆功能
commit 7c688ea99317b740fe49193a930a42bcc1056fce
Author: Shen Siwei <[email protected]>
Date: Tue Oct 13 09:45:41 2015 +0800
initial commit
git show
显示某个commit,例如, 我希望看到上面的 "增加了登陆功能" 的commit 所对应的 代码, 就可以:
$ git show e4b9
注意: 这里不须要写出完整的40位commit号,在可以准确定位commit的情况下, 给出前4位字符就可以了。
git diff
如果file1 文件有了改动,那么我们就可以查看它的变动:
$ git diff file1
也可以显示当前工作区的所有文件的改动:
$ git diff .
$ git diff # 也可以直接把后面的 . 省略掉
也可以显示某两个commit 之间的改动:
$ git diff <1#提交> <2#提交>
Git rebase 笔记 ( git rebase introduction)
前几天的一个文章,我提到 自己比较偏好 git-merge. 现在看起来,这个问题不该带 有个人偏好。而是需要根据情况,来选择使用 git rebase /merge
merge 与 rebase的区别
merge: 会保留时间线。 适合需要保持时间线的场合。 (it would keep the commit order for each branch )
rebase: 不会保留时间线。 适合按照 feature 来查看log的场合。比如,我现在同时在3个分支上工作,但是为了方便代码的提交,我会在一个完成之后再提交另外一个分支/功能模块。所以在这样的情况下使用rebase使得代码更加容易回滚,以及查看log. ( the origin commit would be removed and a new commit would be generated . if you are working on 3 branches at the same time, and want to merge the code till the whole module/branch is done, just use this 'rebase' )
简单用法:
$ git rebase master
这样就会把 master上的最后一个commit, 做为当前 分支的commit 的基础。
更多详细,参考 man git-rebase. ( for more details, please refer to 'man git-rebase' )